/*
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2006-2007 Intel Corporation. All Rights Reserved.
//
*/
#include "umc_defs.h"
#if defined (UMC_ENABLE_DVHD_VIDEO_DECODER)

#include <ippi.h>
#include <ipps.h>
#include <ippvc.h>
#include "umc_dv100_decoder.h"
#include "umc_video_data.h"
#include "umc_dv_internal.h"
#include "vm_event.h"
#include "vm_thread.h"
#include "vm_sys_info.h"
#include "umc_dv_decoder.h"
#include "umc_memory_allocator.h"

#ifdef _OPENMP
#include "omp.h"
#endif

namespace UMC
{

DV100VideoDecoder::DV100VideoDecoder(void) :
    m_pHuffTable(NULL),
    m_ppDecodedDCTBlocks(NULL),
    m_pDestination(NULL),
    m_System(SystemUnknown),
    m_pInternalFrameBuffer(NULL),
    m_pThreads(NULL),
    m_pStartEvents(NULL),
    m_pStopEvents(NULL),
    m_pThreadsID(NULL),
    m_DCTBlocksBufferMID(0),
    m_InternalFrameBufferMID(0),
    m_nSystem720_LastDecodedChannel(0),
    m_shortCutModeFlag(0),
    m_bInitSuccess(false)

{
    vm_event_set_invalid(&m_Quit);
}

DV100VideoDecoder::~DV100VideoDecoder(void)
{
}

static const Ipp32s DV100_VideoSegmentSize = 64 * 8 * 5 * sizeof(Ipp16s);

// Initialize DV decoder
Status DV100VideoDecoder::Init(BaseCodecParams *lpInit)
{
    Ipp32s ThreadNum;
    VideoDecoderParams *pParams = DynamicCast<VideoDecoderParams> (lpInit);

    if (NULL == pParams)
        return UMC_ERR_NULL_PTR;

    Close();

    // Initializes external memory allocator
    if (UMC_OK != BaseCodec::Init(lpInit))
        return UMC_ERR_INIT;

    if(pParams->numThreads == 0)
    {
#ifdef _OPENMP
        m_nNumberOfThreads = omp_get_max_threads();
#else //_OPENMP
        m_nNumberOfThreads = vm_sys_info_get_cpu_num();
#endif //_OPENMP
    }
    else
        m_nNumberOfThreads = pParams->numThreads;

    if (0 >= m_nNumberOfThreads)
        m_nNumberOfThreads = 1;
    else if (8 < m_nNumberOfThreads)
        m_nNumberOfThreads = 8;

    //Alloc internal buffers for DCT blocks
    if( m_pMemoryAllocator->Alloc(&m_DCTBlocksBufferMID,
                                  DV100_VideoSegmentSize * m_nNumberOfThreads,
                                  UMC_ALLOC_PERSISTENT,
                                  16) != UMC_OK )
        return UMC_ERR_ALLOC;

    m_ppDecodedDCTBlocks = new Ipp16s* [m_nNumberOfThreads];
    if (NULL == m_ppDecodedDCTBlocks)
        return UMC_ERR_ALLOC;

    // create threading tools
    if (1 < m_nNumberOfThreads)
    {
        // create quit event
        if (VM_OK != vm_event_init(&m_Quit, 1, 0))
            return UMC_ERR_INIT;

        // create start event(s)
        m_pStartEvents = new vm_event[m_nNumberOfThreads];
        if (NULL == m_pStartEvents)
            return UMC_ERR_ALLOC;
        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
        {
            vm_event_set_invalid(m_pStartEvents + ThreadNum);
            if (VM_OK != vm_event_init(m_pStartEvents + ThreadNum, 0, 0))
                return UMC_ERR_INIT;
        }

        // create stop event(s)
        m_pStopEvents = new vm_event[m_nNumberOfThreads];
        if (NULL == m_pStopEvents)
            return UMC_ERR_ALLOC;
        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
        {
            vm_event_set_invalid(m_pStopEvents + ThreadNum);
            if (VM_OK != vm_event_init(m_pStopEvents + ThreadNum, 0, 0))
                return UMC_ERR_INIT;
        }

        // allocate thread ID(s)
        m_pThreadsID = new THREAD_ID[m_nNumberOfThreads];
        if (NULL == m_pThreadsID)
            return UMC_ERR_ALLOC;

        // starting thread(s)
        m_pThreads = new vm_thread[m_nNumberOfThreads];
        if (NULL == m_pThreads)
            return UMC_ERR_ALLOC;
        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
        {
            vm_thread_set_invalid(m_pThreads + ThreadNum);
            m_pThreadsID[ThreadNum].m_nNumber = ThreadNum;
            m_pThreadsID[ThreadNum].m_lpOwner = this;
            if (0 == vm_thread_create(m_pThreads + ThreadNum, ThreadWorkingRoutine, m_pThreadsID + ThreadNum))
                return UMC_ERR_INIT;
        }

    }

    m_nHeight = pParams->info.clip_info.height;
    m_nWidth = pParams->info.clip_info.width;

    // check parameters
    if ( !((SYSTEM_1080_WIDTH == m_nWidth && SYSTEM_1080_HEIGHT == m_nHeight) ||
         (SYSTEM_720_WIDTH == m_nWidth && SYSTEM_720_HEIGHT == m_nHeight) ||
         (960 == m_nWidth && SYSTEM_720_HEIGHT == m_nHeight) ||
         (960/2 == m_nWidth && 720/2 == m_nHeight) ||
         (960/4 == m_nWidth && 720/4 == m_nHeight) ||
         (960/8 == m_nWidth && 720/8 == m_nHeight) ||
         (960 == m_nWidth && 720/2 == m_nHeight) ||
         (960 == m_nWidth && 720/4 == m_nHeight) ||
         (1280 == m_nWidth && SYSTEM_1080_HEIGHT == m_nHeight) ||
         (1280/2 == m_nWidth && 1080/2 == m_nHeight) ||
         (1280/4 == m_nWidth && 1080/4 == m_nHeight) ||
         (1280/8 == m_nWidth && 1080/8 == m_nHeight) ||
         (1280 == m_nWidth && 1080/2 == m_nHeight) ||
         (1280 == m_nWidth && 1080/4 == m_nHeight) ||
         (1440 == m_nWidth && SYSTEM_1080_HEIGHT == m_nHeight) ||
         (1440/2 == m_nWidth && 1080/2 == m_nHeight) ||
         (1440/4 == m_nWidth && 1080/4 == m_nHeight) ||
         (1440/8 == m_nWidth && 1080/8 == m_nHeight) ||
         (1440 == m_nWidth && 1080/2 == m_nHeight)  ||
         (1440 == m_nWidth && 1080/4 == m_nHeight)  ))
    {
        return UMC_ERR_INVALID_STREAM;
    }

    m_ClipInfo = pParams->info;

    //Check and set Short Cut Modes, if possible
    //m_shortCutModeFlag = 0; - no, full resolution
    //m_shortCutModeFlag = 1; - 1/2 width and 1/2 height, quarter resolution
    //m_shortCutModeFlag = 2; - 1/4 width and 1/4 height
    //m_shortCutModeFlag = 3; - 1/8 width and 1/8 height, DC only
    //m_shortCutModeFlag = 4; - full width and 1/2 height
    //m_shortCutModeFlag = 5; - full width and 1/4 height
    //NB! Short cut modes required YUY2 output color format
    //(as YUY2 dv100 decoder's internal format)
    if ( ((960/2 == m_nWidth) && (720/2 == m_nHeight)) ||
         ((1440/2 == m_nWidth) && (1080/2 == m_nHeight)) ||
         ((1280/2 == m_nWidth) && (1080/2 == m_nHeight)) )
    {
        m_shortCutModeFlag = 1;
    }

    if ( ((960/4 == m_nWidth) && (720/4 == m_nHeight)) ||
         ((1440/4 == m_nWidth) && (1080/4 == m_nHeight)) ||
         ((1280/4 == m_nWidth) && (1080/4 == m_nHeight)) )
    {
        m_shortCutModeFlag = 2;
    }
    if ( ((960/8 == m_nWidth) && (720/8 == m_nHeight)) ||
         ((1440/8 == m_nWidth) && (1080/8 == m_nHeight)) ||
         ((1280/8 == m_nWidth) && (1080/8 == m_nHeight)) )
    {
        m_shortCutModeFlag = 3;
    }
    if ( ((960 == m_nWidth) && (720/2 == m_nHeight)) ||
         ((1440 == m_nWidth) && (1080/2 == m_nHeight)) ||
         ((1280 == m_nWidth) && (1080/2 == m_nHeight)) )
    {
        m_shortCutModeFlag = 4;
    }
    if ( ((960 == m_nWidth) && (720/4 == m_nHeight)) ||
         ((1440 == m_nWidth) && (1080/4 == m_nHeight)) ||
         ((1280 == m_nWidth) && (1080/4 == m_nHeight)) )
    {
        m_shortCutModeFlag = 5;
    }

    //Codec always alloc maximal internal buffer 1920*1080*2 = 4147200 byte
    //Internal color format is YUY2
    m_nDestPitch = align_value<Ipp32s> (2 * 1920, WIDTH_ALIGN);

    if( m_pMemoryAllocator->Alloc(&m_InternalFrameBufferMID,
                                   m_nDestPitch * m_nHeight,
                                   UMC_ALLOC_PERSISTENT,
                                   16) != UMC_OK )
      return UMC_ERR_ALLOC;

    m_PostProcessing = pParams->pPostProcessing;

    if (ippStsOk != ippiInitAllocHuffmanTable_DV_32u(DVVideoDecoder::dvTable1, DVVideoDecoder::dvTable2, &m_pHuffTable))
        return UMC_ERR_ALLOC;

    m_bInitSuccess = true;

    return UMC_OK;
}

// Close decoder
Status DV100VideoDecoder::Close()
{
    Ipp32s ThreadNum;
    // let all threads exit
    if (m_pThreads)
    {
        vm_event_signal(&m_Quit);

        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
            vm_event_signal(m_pStartEvents + ThreadNum);

        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
        {
            if (vm_thread_is_valid(m_pThreads + ThreadNum))
                vm_thread_wait(m_pThreads + ThreadNum);
        }
    }

    // delete all threading tools
    if (vm_event_is_valid(&m_Quit))
    {
        vm_event_destroy(&m_Quit);
        vm_event_set_invalid(&m_Quit);
    }

    if (m_pThreads)
    {
        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
            vm_thread_set_invalid(m_pThreads + ThreadNum);

        delete [] m_pThreads;
        m_pThreads = NULL;
    }

    if (m_pStartEvents)
    {
        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
        {
            if (vm_event_is_valid(m_pStartEvents + ThreadNum))
            {
                vm_event_destroy(m_pStartEvents + ThreadNum);
                vm_event_set_invalid(m_pStartEvents + ThreadNum);
            }
        }

        delete [] m_pStartEvents;
        m_pStartEvents = NULL;
    }

    if (m_pStopEvents)
    {
        for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
        {
            if (vm_event_is_valid(m_pStopEvents + ThreadNum))
            {
                vm_event_destroy(m_pStopEvents + ThreadNum);
                vm_event_set_invalid(m_pStopEvents + ThreadNum);
            }
        }

        delete [] m_pStopEvents;
        m_pStopEvents = NULL;
    }

    if (m_pThreadsID)
    {
        delete [] m_pThreadsID;
        m_pThreadsID = NULL;
    }


    if(m_pHuffTable != NULL)
        ippiFreeHuffmanTable_DV_32u(m_pHuffTable);
    m_pHuffTable = NULL;

    if(m_ppDecodedDCTBlocks != NULL)
        delete[] m_ppDecodedDCTBlocks;
    m_ppDecodedDCTBlocks = NULL;

    if( m_pMemoryAllocator )
    {
        if(m_DCTBlocksBufferMID != 0)
        {
            m_pMemoryAllocator->Free(m_DCTBlocksBufferMID);
            m_DCTBlocksBufferMID= 0;
        }

        if(m_InternalFrameBufferMID != 0)
        {
            m_pMemoryAllocator->Free(m_InternalFrameBufferMID);
            m_InternalFrameBufferMID = 0;
        }
    }

    // delete memory allocator if internal
    BaseCodec::Close();

    m_System = SystemUnknown;

    return UMC_OK;
}

//Distinguish 1080/60i and 1080/50i systems
Status DV100VideoDecoder::GetSystemSubtype()
{
    Ipp8u vaux_framerate = 0;
    Ipp8u vaux_system = 0;
    Ipp8u byte_PC0_of_VAUX  = 0;
    Ipp8u byte_PC3_of_VAUX = 0;
    //VAUX section 3, offfset from start of a DIF sequence 3*80 = 240 byte
    //Length of each section is 80 byte
    //Offset to VAUX source pack is 3 + 5*9 = 48 byte
    byte_PC0_of_VAUX = (Ipp8u) m_pStreamStart[240 + 48 + 0 + 2*80];
    byte_PC3_of_VAUX = (Ipp8u) m_pStreamStart[240 + 48 + 3 + 2*80];

    //check VAUX section
    if (0x60 != byte_PC0_of_VAUX)
        return UMC_ERR_INVALID_STREAM;
    else
    {
        vaux_framerate = (byte_PC3_of_VAUX>>5) &0x1;
        vaux_system = byte_PC3_of_VAUX &0x1f;
        if ((0 == vaux_framerate) &&( (0x14 == vaux_system) || (0x15 == vaux_system)) )
        {
            m_System = System1080_60i;
            m_nWidth = SYSTEM_1080_60I_WIDTH_NOT_STRETCHED;
            m_nHeight = SYSTEM_1080_HEIGHT;
        }
        if ((0 == vaux_framerate) && (0x18 == vaux_system) )
        {
            m_System = System720_60p;
            m_nWidth = SYSTEM_720_WIDTH_NOT_STRETCHED;
            m_nHeight = SYSTEM_720_HEIGHT;
        }
        if ((1 == vaux_framerate) && (0x14 == vaux_system) )
        {
            m_System = System1080_50i;
            m_nWidth = SYSTEM_1080_50I_WIDTH_NOT_STRETCHED;
            m_nHeight = SYSTEM_1080_HEIGHT;
        }
        if ((1 == vaux_framerate) && (0x18 == vaux_system) )
        {
            m_System = System720_50p;
            m_nWidth = SYSTEM_720_WIDTH_NOT_STRETCHED;
            m_nHeight = SYSTEM_720_HEIGHT;
        }
    }

    m_ClipInfo.clip_info.width = m_nWidth;
    m_ClipInfo.clip_info.height = m_nHeight;

    return UMC_OK;
}

const size_t DIFSequenceLength = 150 * 80; //150 DIF blocks by 80 bytes

// Decode next frame
Status DV100VideoDecoder::GetFrame(MediaData *pInData, MediaData *pOutData)
{
    VideoData *pVideoDataOut = DynamicCast<VideoData> (pOutData);
    Status status = UMC_OK;
    Ipp32s ThreadNum;

       if (!m_bInitSuccess)
        return UMC_ERR_NOT_INITIALIZED;

    if ((NULL == pVideoDataOut))
        return UMC_ERR_NULL_PTR;

    if ((NULL == pInData))
    {
        return UMC_ERR_NOT_ENOUGH_DATA;
    }

    m_pStreamStart = reinterpret_cast<Ipp8u *> (pInData->GetDataPointer());

    //Check modes(systems) of DV100
    status = GetSystemSubtype();
    if(UMC_OK != status)
        return status;
    //NB! Short cut modes required YUY2 output color format
    //(as YUY2 dv100 decoder's internal format)
    if (1 <= m_shortCutModeFlag)
        if (YUY2 != pVideoDataOut->GetColorFormat())
            return UMC_ERR_INVALID_PARAMS;

    if (!m_PostProcessing) {
      m_PostProcessing = m_allocatedPostProcessing = createVideoProcessing();
    }

#if 0
    if (m_allocatedPostProcessing /* post processing is default */ &&
        YUY2 == lpVData->GetColorFormat() &&
        m_nWidth == pVideoDataOut->GetWidth() &&
        m_nHeight == pVideoDataOut->GetHeight())
    {
      m_pDestination = (Ipp8u*) pVideoDataOut->GetPlanePointer(0);
      m_nDestPitch = pVideoDataOut->GetPlanePitch(0);
    } else
#endif
    {
       m_pInternalFrameBuffer = (Ipp8u*) m_pMemoryAllocator->Lock(m_InternalFrameBufferMID);
       if( m_pInternalFrameBuffer == NULL )
           return UMC_ERR_NULL_PTR;

       if (0 == m_shortCutModeFlag)
       {
           m_pDestination = m_pInternalFrameBuffer;
           m_nDestPitch = align_value<Ipp32s> (2 * m_nWidth, WIDTH_ALIGN);
       }
       else
       {
           m_pDestination = (Ipp8u *)pVideoDataOut->GetPlanePointer(0);
           m_nDestPitch = pVideoDataOut->GetPlanePitch(0);
       }
    }

    Ipp8u *pDCTBlocksBuffer = (Ipp8u*)m_pMemoryAllocator->Lock(m_DCTBlocksBufferMID);
    if(pDCTBlocksBuffer == NULL)
        return UMC_ERR_NULL_PTR;

    for (ThreadNum = 0; ThreadNum < m_nNumberOfThreads; ThreadNum += 1)
        m_ppDecodedDCTBlocks[ThreadNum] = (Ipp16s*)( ((Ipp8u*)(pDCTBlocksBuffer)) + DV100_VideoSegmentSize * ThreadNum );

    // start additional thread(s)
    for (ThreadNum = 1; ThreadNum < m_nNumberOfThreads; ThreadNum += 1)
        vm_event_signal(m_pStartEvents + ThreadNum);

    DecomressDIFSequences(0);

    // wait additional thread(s)
    for (ThreadNum = 1;ThreadNum < m_nNumberOfThreads;ThreadNum += 1)
        vm_event_wait(m_pStopEvents + ThreadNum);

    m_pMemoryAllocator->Unlock(m_DCTBlocksBufferMID);
#ifdef _DEBUG
    memset(m_ppDecodedDCTBlocks, 0, sizeof(Ipp16u*) * m_nNumberOfThreads);
#endif

    if ((m_System == System720_60p)|| (m_System == System720_50p))
    {
        if(m_nSystem720_LastDecodedChannel == 0)
            m_nSystem720_LastDecodedChannel = 2;
        else
            m_nSystem720_LastDecodedChannel = 0;
    }

    pVideoDataOut->SetFrameType(I_PICTURE);
    pVideoDataOut->SetTime(pInData->GetTime());

    if (m_LastDecodedFrame.GetColorFormat() != YUY2) {
      m_LastDecodedFrame.Init(m_nWidth,
                              m_nHeight,
                              YUY2);
    }
    m_LastDecodedFrame.SetFrameType(I_PICTURE);
    m_LastDecodedFrame.SetTime(pInData->GetTime());
    m_LastDecodedFrame.SetPlanePointer((void*)m_pDestination, 0);
    m_LastDecodedFrame.SetPlanePitch(m_nDestPitch, 0);
    if (m_pDestination == m_pInternalFrameBuffer)
    {
      status = m_PostProcessing->GetFrame(&m_LastDecodedFrame, pVideoDataOut);
      m_pMemoryAllocator->Unlock(m_InternalFrameBufferMID);
      m_pInternalFrameBuffer = NULL;
    }

    if (System1080_60i == m_System) {
        pInData->MoveDataPointer(120000*4);
        pInData->SetTime(pInData->GetTime() + 2.0/59.96);
    }
    else if(System1080_50i == m_System)
    {
        pInData->MoveDataPointer(144000*4);
        pInData->SetTime(pInData->GetTime() + 1.0/25);
    }
    else if (System720_60p == m_System)
    {
        pInData->MoveDataPointer(120000*2);
        pInData->SetTime(pInData->GetTime() + 1.0/59.96);
    }
    else if (System720_50p == m_System)
    {
        pInData->MoveDataPointer(144000*2);
        pInData->SetTime(pInData->GetTime() + 1.0/25);
    }

    return status;
}

void DV100VideoDecoder::DecomressDIFSequences(Ipp32s nThreadNum)
{
    Ipp32s nChannelNum, nDIFSeqNum, nVideoSegmentNum, nDIFSeqInChannel;
    Ipp32s nDIFSeqInFrame, nStartDIFSeqNum, nEndDIFSeqNum;
    Ipp16s TempBlock[64];

    switch(m_System)
    {
        case System1080_50i: nDIFSeqInFrame = 12*4; break;
        case System1080_60i: nDIFSeqInFrame = 10*4; break;
        case System720_60p:  nDIFSeqInFrame = 10*2; break;
        case System720_50p:  nDIFSeqInFrame = 12*2; break;
        default: nDIFSeqInFrame = 0;
    }

    nStartDIFSeqNum = nDIFSeqInFrame * nThreadNum / m_nNumberOfThreads;
    nEndDIFSeqNum   = nDIFSeqInFrame * (nThreadNum+1) / m_nNumberOfThreads;

    //Align quant & weight matrix's
    Ipp16s *pQuantizeMatrixTable;
    Ipp16s bufferForWeightMatrix[2*4*64];
    Ipp64s ten = -10;
    pQuantizeMatrixTable = (Ipp16s *) (((Ipp64s) (bufferForWeightMatrix) & ten) + 0x10);
    ippsCopy_16s((Ipp16s *) LumaQuantizeMatrix_720System, pQuantizeMatrixTable + 64*0, 64);
    ippsCopy_16s((Ipp16s *) ChromaQuantizeMatrix_720System, pQuantizeMatrixTable + 64 *1, 64);
    ippsCopy_16s((Ipp16s *) LumaQuantizeMatrix_1080System, pQuantizeMatrixTable + 64 *2, 64);
    ippsCopy_16s((Ipp16s *) ChromaQuantizeMatrix_1080System, pQuantizeMatrixTable + 64 *3, 64);

    Ipp16s* pDecodedDCTBlocks = m_ppDecodedDCTBlocks[nThreadNum];

    union
    {
        BlockParam BlParam[40];
        Ipp32u BlParamBuffer[40];
    };

    nDIFSeqInChannel = ((m_System == System1080_50i)|| (m_System == System720_50p)) ? 12 : 10;
    for(nDIFSeqNum=nStartDIFSeqNum; nDIFSeqNum < nEndDIFSeqNum; nDIFSeqNum++)
    {
        if(m_System == System1080_50i && (nDIFSeqNum == 23 || nDIFSeqNum == 35 || nDIFSeqNum == 47))
            continue;
        if ((m_System == System720_50p) &&((nDIFSeqNum == 10)|| (nDIFSeqNum == 11) || (nDIFSeqNum == 22) || (nDIFSeqNum == 23)))
            continue;

        nChannelNum = nDIFSeqNum / nDIFSeqInChannel;
        for(nVideoSegmentNum=0; nVideoSegmentNum < 27; nVideoSegmentNum++)
        {
            Ipp32s nMBNum, nDCTBlockNum, bl_index, eob;
            Ipp16s *pCurrDCTBlock;
            Ipp8u *pVideoSegmentStart = m_pStreamStart +
                                        nDIFSeqNum * DIFSequenceLength +
                                        //nChannelNum * ChannelLength +
                                        //(nDIFSeqNum % nDIFSeqInChannel) * DIFSequenceLength +
                                        (6 + (nVideoSegmentNum/3 + 1) + nVideoSegmentNum*5) * 80;

            memset(pDecodedDCTBlocks, 0, DV100_VideoSegmentSize);
            if (3 != m_shortCutModeFlag)
                ippiHuffmanDecodeSegment_DV100_8u16s(pVideoSegmentStart,
                                                     DVVideoDecoder::_INTERNAL_DEZIGZAG_TABLE_0,//ZigzagTable,
                                                     m_pHuffTable,
                                                     pDecodedDCTBlocks,
                                                     BlParamBuffer);
            else //DC decoding only
            {
                Ipp8u *pCurrVideoSegment = pVideoSegmentStart;
                Ipp16s tmpDC = 0;
                for ( nMBNum = 0; nMBNum < 5; nMBNum++)
                {
                    //Ipp32s stream_index = 4, block_index = 0;;
                    //while ( stream_index <=64 )
                    //{
                    //    tmpDC = (pCurrVideoSegment[stream_index] << 8)
                    //                                            + pCurrVideoSegment[stream_index + 1];
                    //    pDecodedDCTBlocks[(nMBNum*8 + block_index)*64] = ((Ipp16s)((pCurrVideoSegment[stream_index] << 8)
                    //                                            + pCurrVideoSegment[stream_index + 1])) >>(5+3);
                    //    stream_index +=10;
                    //    block_index++;
                    //}
                    //Proved to the best
                    tmpDC = (pCurrVideoSegment[04] << 8) + pCurrVideoSegment[04 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 0)*64] = tmpDC >> (5+3);
                    tmpDC = (pCurrVideoSegment[14] << 8) + pCurrVideoSegment[14 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 1)*64] = tmpDC >> (5+3);
                    tmpDC = (pCurrVideoSegment[24] << 8) + pCurrVideoSegment[24 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 2)*64] = tmpDC >> (5+3);
                    tmpDC = (pCurrVideoSegment[34] << 8) + pCurrVideoSegment[34 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 3)*64] = tmpDC >> (5+3);
                    tmpDC = (pCurrVideoSegment[44] << 8) + pCurrVideoSegment[44 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 4)*64] = tmpDC >> (5+3);
                    tmpDC = (pCurrVideoSegment[54] << 8) + pCurrVideoSegment[54 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 5)*64] = tmpDC >> (5+3);
                    tmpDC = (pCurrVideoSegment[64] << 8) + pCurrVideoSegment[64 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 6)*64] = tmpDC >> (5+3);
                    tmpDC = (pCurrVideoSegment[72] << 8) + pCurrVideoSegment[72 + 1];
                    pDecodedDCTBlocks[(nMBNum*8 + 7)*64] = tmpDC >> (5+3);
                    pCurrVideoSegment +=80;
                }
            }

            if (3 != m_shortCutModeFlag)
            {
            pCurrDCTBlock = pDecodedDCTBlocks;
            for(nMBNum = 0; nMBNum < 5; nMBNum++)
            {
                Ipp8u QNO = BlParam[nMBNum * 8].qno;
                Ipp8u M0 = BlParam[nMBNum * 8].m0;// In contrast to DVSD in dv100 the same DCT mode
                //(8-8-frame or 8-8-field) is applied to all DCT blocks in a Macro block. The DCT
                //mode is stored in the FIRST DCT BLOCK of the macro block. m0 values of other
                //DCT blocks ARE IGNORED.

                for(nDCTBlockNum = 0; nDCTBlockNum < 8; nDCTBlockNum++)
                {
                    Ipp8u ClassNumber = BlParam[nMBNum * 8 + nDCTBlockNum].cl;
                    Ipp32s QuantStep = QuantizationSteps[QNO*4 + ClassNumber];
                    bl_index = (BlParamBuffer[nMBNum * 8 + nDCTBlockNum] >> 8) & 0xff;
                    eob = BlParamBuffer[nMBNum * 8 + nDCTBlockNum]  & 0x01;

                    //Ipp32s ElemNum;
                    const Ipp16s *pQuantizeTable;

                    //Dequantize block
                    //if ((m_System == System720_60p) || (m_System == System720_50p))
                    //    pQuantizeTable = (nDCTBlockNum < 4) ? LumaQuantizeMatrix_720System : ChromaQuantizeMatrix_720System;
                    //else
                    //    pQuantizeTable = (nDCTBlockNum < 4) ? LumaQuantizeMatrix_1080System : ChromaQuantizeMatrix_1080System;

                    if ((m_System == System720_60p) || (m_System == System720_50p))
                        pQuantizeTable = (nDCTBlockNum < 4) ? pQuantizeMatrixTable + 0*64 : pQuantizeMatrixTable + 1*64;
                    else
                        pQuantizeTable = (nDCTBlockNum < 4) ? pQuantizeMatrixTable + 2*64 : pQuantizeMatrixTable + 3*64;

                    //Do quantization and weighting
                    ippiQuantWeightBlockInv_DV100_16s_C1I(pCurrDCTBlock, (Ipp16s *)pQuantizeTable, QuantStep);

                    //Do inverse DCT
                    //Mainline, full resolution
                    if ((0 == m_shortCutModeFlag) || (3 <= m_shortCutModeFlag))
                    {
                        if ((eob == 0)||( 10 > bl_index))
                        {
                            ippiDCT8x8Inv_4x4_16s_C1I(pCurrDCTBlock);
                        }
                        else
                            ippiDCT8x8Inv_16s_C1I(pCurrDCTBlock);
                    }
                    //ShortCut mode: 1/2 width and 1/2 height, quarter resolution
                    else if ((1 == m_shortCutModeFlag) || (2 == m_shortCutModeFlag))
                        ippiDCT8x8To4x4Inv_16s_C1I(pCurrDCTBlock);

                    pCurrDCTBlock += 64;
                }//for nDCTBlockNum = 0 to 7

                if(M0 == 1)
                {
                    Ipp16s x,y;

                    //Rearrange pixeles in vertically adjacent DCT blocks
                    for(nDCTBlockNum =0; nDCTBlockNum < 4; nDCTBlockNum++)
                    {
                        Ipp16s *pUpperDCTBlock, *pLowerDCTBlock;
                        if(nDCTBlockNum < 2)
                        {
                            pUpperDCTBlock = pDecodedDCTBlocks + (8*nMBNum + nDCTBlockNum)*64;
                            pLowerDCTBlock = pUpperDCTBlock + 2*64;
                        }
                        else
                        {
                            pUpperDCTBlock = pDecodedDCTBlocks + (8*nMBNum + nDCTBlockNum*2)*64;
                            pLowerDCTBlock = pUpperDCTBlock + 64;
                        }
                        memcpy(TempBlock, pUpperDCTBlock, 64*sizeof(Ipp16s));

                        if ((0 == m_shortCutModeFlag) || (3 <= m_shortCutModeFlag))
                        {
                            for(y=0; y<4; y++)
                            {
                                for(x=0; x<8; x++)
                                {
                                    pUpperDCTBlock[ y*2*8 + x]    = TempBlock[y*8 + x];
                                    pUpperDCTBlock[(y*2+1)*8 + x] = pLowerDCTBlock[y*8 + x];
                                }
                            }

                            for(y=0; y<4; y++)
                            {
                                for(x=0; x<8; x++)
                                {
                                    pLowerDCTBlock[ y*2*8 + x]    = TempBlock[(4+y)*8 + x];
                                    pLowerDCTBlock[(y*2+1)*8 + x] = pLowerDCTBlock[(4+y)*8 + x];
                                }
                            }
                        }//if ((0 == m_shortCutModeFlag) || (3 <= m_shortCutModeFlag))
                        else if ((1 == m_shortCutModeFlag) || (2 == m_shortCutModeFlag))
                        {
                            for(y=0; y<2; y++)
                            {
                                for(x=0; x<4; x++)
                                {
                                    pUpperDCTBlock[ y*2*4 + x]    = TempBlock[y*4 + x];
                                    pUpperDCTBlock[(y*2+1)*4 + x] = pLowerDCTBlock[y*4 + x];
                                }
                            }

                            for(y=0; y<2; y++)
                            {
                                for(x=0; x<4; x++)
                                {
                                    pLowerDCTBlock[ y*2*4 + x]    = TempBlock[(2+y)*4 + x];
                                    pLowerDCTBlock[(y*2+1)*4 + x] = pLowerDCTBlock[(2+y)*4 + x];
                                }
                            }

                        }//else if ((1 == m_shortCutModeFlag) || (2 == m_shortCutModeFlag))

                    }//for(nDCTBlockNum =0; nDCTBlockNum < 4; nDCTBlockNum++)
                }//if(M0 == 1)

            }//for nMBNum = 0 to 4
            }//if (3 != m_shortCutModeFlag)

            if ((m_System == System720_60p) || (m_System == System720_50p))
            {
                Store_System720_60p_Segment(pDecodedDCTBlocks, nChannelNum + m_nSystem720_LastDecodedChannel, (nDIFSeqNum % nDIFSeqInChannel), nVideoSegmentNum);
            }
            else if(m_System == System1080_50i)
            {
                Store_System1080_50i_Segment(pDecodedDCTBlocks, nChannelNum, (nDIFSeqNum % nDIFSeqInChannel), nVideoSegmentNum);
            }
            else
            {
                Store_System1080_60i_Segment(pDecodedDCTBlocks, nChannelNum, (nDIFSeqNum % nDIFSeqInChannel), nVideoSegmentNum);
            }

        }//for nVideoSegmentNum = 0 to 26
    }//for nDIFSeqNum = 0 to nDIFSeqInChannel-1 (9 or 11)
    //}//for nChannelNum = 0 to 1 or 3
}


Ipp32u DV100VideoDecoder::ThreadWorkingRoutine(void *lpv)
{

    THREAD_ID *pThreadId;
    DV100VideoDecoder *pOwner;

    // check error(s)
    if (NULL == lpv)
        return 0x0bad;

    pThreadId = reinterpret_cast<THREAD_ID *> (lpv);
    pOwner = reinterpret_cast<DV100VideoDecoder *> (pThreadId->m_lpOwner);

    // wait for start
    vm_event_wait(pOwner->m_pStartEvents + pThreadId->m_nNumber);

    while (VM_TIMEOUT == vm_event_timed_wait(&(pOwner->m_Quit), 0))
    {
        // decompress part of frame
        pOwner->DecomressDIFSequences(pThreadId->m_nNumber);

        // set finish
        vm_event_signal(pOwner->m_pStopEvents + pThreadId->m_nNumber);

        // wait for start
        vm_event_wait(pOwner->m_pStartEvents + pThreadId->m_nNumber);
    };

    return pThreadId->m_nNumber;
}
// Get decoder performance
Status DV100VideoDecoder::GetPerformance(Ipp64f *perf)
{
    if (perf != NULL)
        *perf = 1.0;

    return UMC_OK;
}

// Reset decoder into initial state
Status DV100VideoDecoder::Reset()
{
   if (!m_bInitSuccess)
        return UMC_ERR_NOT_INITIALIZED;
    else
        return UMC_OK;
}

Status DV100VideoDecoder::GetInfo(BaseCodecParams* info)
{
    VideoDecoderParams *pParams;
    if(info == NULL)
        return UMC_ERR_NULL_PTR;

    if (m_bInitSuccess == false)
        return UMC_ERR_NOT_INITIALIZED;

    pParams = DynamicCast<VideoDecoderParams> (info);

    if ((NULL != pParams) && (m_bInitSuccess == true) ) {
        pParams->info = m_ClipInfo;
        pParams->lpMemoryAllocator = m_pMemoryAllocator;
        pParams->pPostProcessing = m_PostProcessing;
        pParams->numThreads = m_nNumberOfThreads;
    }
    return UMC_OK;

} // Status DV100VideoDecoder::GetInfo(void)

}//namespace UMC

#endif //(UMC_ENABLE_DVHD_VIDEO_DECODER)
